<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>姓名案例_watch实现</title>
    <script src="../js/vue.js"></script>
</head>
<body>
    <!--
        监视属性 watch:
            1. 当被监视的属性变化时，回调函数自动调用，进行相关操作
            2. 属性必须存在，才能监视！
               与 computed 的最大区别在于，
               computed 是计算一个不存在的属性，watch 是监视的属性必须存在
            3. 监视的两种写法：
                    (1). new Vue 时传入 watch 配置
                    (2). 通过 vm.$watch 监视
            4. watch 可以监视 computed 的计算属性
            5. watch 监视的属性，不论是否出现在页面，都会触发 getter、setter 函数，非常神奇
            computed 和 watch 之间的区别：
                1. 只要是 computed 能完全的功能，watch 都可以完成(watch 的底层用到了 setter)
                2. watch 能完成的功能，computed 不一定能完成，例如：watch 可以进行异步操作
            备注：
                1. 所有被 Vue 所调用（管理）函数，都不要写箭头函数 --- 例如： watch 中的函数，computed 中的函数
                2. 所有不是被 Vue 所调用的函数，都要写成箭头函数 --- 例如：定时器的回调，ajax 的回调等等
                3. 从设计上讲，computed 计算属性必须立刻计算立刻返回值，因为它是以返回值的形式被展示的，不可能执行异步操作
                   但是 watch 维护的是一个属性，它是定时修改那个属性的值
                   这就好比，1分钟后修改值和1分钟后执行返回语句，前者是可以做到的，后者不可能做到。
                   其实这个需求在 egret 画图的项目中也遇到过，那时候想每一个动画延迟几秒钟出现，但是需要用到 await 很麻烦
                4. watch 就是 Vue 给我们提供的一个监控数据改变的手段，至于数据改变后要做什么，得看具体的业务逻辑
                   例如
                        需要新、旧两个值进行比较，决定接下来要干什么
                        不需要值，只要数据改变了，就要发请求
    -->
    <div id="root">
        姓：<input type="text" v-model="first_name" placeholder=""><br/>
        名：<input type="text" v-model="last_name" placeholder=""><br/>
        <span>全名：{{full_name}}</span>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                first_name: 'Kris',
                last_name: 'Wu',
                full_name: ''
            },
            computed: {
                test: {
                    set(value){
                        console.log("set-被调用了");
                        this.first_name = value;
                    },
                    get(){
                        console.log("get-被调用了");
                        return this.first_name + "!";
                    }
                }
            },
            // 里面的所有属性/函数不会出现在 vm 上，因为它们是用来执行的，不是用来看的
            watch: {
                /*
                 * 1. watch 中的 first_name 是函数，调用时机：当 data 中的 first_name 被改变的时候就调用
                 * 2. watch 中的 first_name 中的 this 是 vm
                 */
                // first_name(newValue, oldValue){
                //     console.log("firstName 被改变了", newValue, oldValue);
                //     this.full_name = newValue + '-' + this.last_name;
                // },
                // first_name:{
                //     // immediate 立刻的 立即的 马上的
                //     immediate: true, // 若 immediate 为 true，则在初始化时就会调用一次
                //     handler(newValue, oldValue){
                //         console.log("firstName 被改变了", newValue, oldValue);
                //         // 未使用的定义 full_name
                //         this.full_name = newValue + '-' + this.last_name;
                //     }
                // },
                // 简写
                last_name(newValue, oldValue){
                    console.log("last_name 被改变了", newValue, oldValue);
                    this.full_name = this.first_name + '-' + newValue;
                },
                test(){
                    console.log("test 被改变了");
                }
            }
        });

        // 第二种写法，类似于 el 也有两种写法 vm.$mount('#root');
        vm.$watch('first_name', {
            // immediate 立刻的 立即的 马上的
            immediate: true, // 若 immediate 为 true，则在初始化时就会调用一次
            handler(newValue, oldValue){
                console.log("firstName 被改变了", newValue, oldValue);
                /*
                 * 实现异步操作
                 * 为什么这里可以写箭头函数呢？
                 * 1. 我们的原则是该 Vue 管理的函数要用非箭头函数，但 setTimeout 应该由 window 调用，而不是 Vue，所以能用箭头函数
                 * 2. 这里必须用箭头函数，为什么呢？因为箭头函数没有 this，这样 this.full_name 就要向上级执行环境去找，找到 handler 环境时发现了 full_name
                 * 3. 但如果用普通函数，则 this 有了明确的指向，this 指向谁呢？指向 window
                 *    由原因1可以得知，setTimeout 由 window 调用的话 this 就会指向 window 而不是 Vue！所以 this.full_name 就添加到 window 身上了！
                 *    所以这里我们必须得用箭头函数消除 this 的指向，逼着 full_name 向上级执行环境去要
                 */
                setTimeout(() => {
                    // 未使用的定义 full_name
                    this.full_name = newValue + '-' + this.last_name;
                }, 1000)
            }
        })
    </script>
</body>
</html>
